package emulator;

import data.Rule;
import data.RuleSet;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.net.*;
import java.util.StringTokenizer;


public class FirewallEmulator{



	public static FileWriter fwo ;
	public static BufferedWriter bwObj; 
	public static void main(String args[]) throws Exception{
		if(args.length == 2){


			fwo = new FileWriter( "output-firewall-log.txt", false );
			// false means we will be writing to the file
			bwObj = new BufferedWriter( fwo );
			RuleSet rulesSet = new RuleSet(args[0]);
			//rulesSet.printRules();

			int port =Integer.parseInt(args[1]);
			//int no =Integer.parseInt(args[2]);
			int no=1;
			DatagramSocket serverSocket = new DatagramSocket(port);
			byte[] receiveData = new byte[1024];
			while(true)
			{
				DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
				serverSocket.receive(receivePacket);
				int len = receivePacket.getLength();
				String sent = new String( receivePacket.getData());
				String sentence = sent.substring(0, len);
				System.out.print("RECEIVED: " + sentence+" ---- ");
				bwObj.write( "RECEIVED: " + sentence+" ---- " );
				bwObj.flush();
				checkPacket(sentence, rulesSet);
				//System.out.println("RECEIVED: " + sentence);
				//no--;
				if(no==0){
					break;
				}

				//add stuff to match and log the results
			}
			serverSocket.close();
			bwObj.close();
		}
		else{
			System.out.println("Wrong num of args -- need 2 args");
		}
	}

	public static void checkPacket(String message, RuleSet rulesSet) throws IOException{
		int packetLength=correctFormat(message);
		if(packetLength == 5){
			//System.out.println("checkpacket");
			String headers[]=new String[5];
			headers=message.split(" ");
			checkRules(headers,rulesSet);
		}
		else{
			System.out.println(" Ill formed input ");
			//log ill-formed input	
			bwObj.write( " Ill formed input " );
			bwObj.flush();
			bwObj.newLine();
			bwObj.flush();
		}
	}

	private static void checkRules(String[] headers, RuleSet rulesSet) throws IOException{
		//check for matching rules
		//log the disposition and packet message and rule number
		//need to check for both IP addresses and both ports
		//System.out.println("checkRules");
		for(int i=0; i<rulesSet.rules.length;i++){
			if(ifMatchIP(rulesSet.rules[i], headers[1]) && ifMatchPort(rulesSet.rules[i],headers[3])){
				//need to check for flag
				if(rulesSet.rules[i].hasModifier){
					if(rulesSet.rules[i].modifier.equals("established")){
						if(headers[4].equalsIgnoreCase("continue") || headers[4].equalsIgnoreCase("end")){
							System.out.println( "--Decision is: "+rulesSet.rules[i].verb+ "-- Rule no is "+ (i+1));
							//log that it matched and the disposition and the packet details
							bwObj.write( "--Decision is: "+rulesSet.rules[i].verb+ "-- Rule no is "+ (i+1));
							bwObj.flush();
							bwObj.newLine();
							bwObj.flush();
							break;
						}
					}
				}
				else{
					System.out.println( "--Decision is: "+rulesSet.rules[i].verb+ "-- Rule no is "+ (i+1));
					//log that it matched and the disposition and the packet details
					bwObj.write( "--Decision is: "+rulesSet.rules[i].verb+ "-- Rule no is "+ (i+1));
					bwObj.flush();
					bwObj.newLine();
					bwObj.flush();
					break;
				}
			}
		}
	}

	private static boolean ifMatchPort(Rule r, String string) {
		//need to consider * as well
		if(r.allPort)
			return true;
		else{
			int p1=getIntVal(r.port);
			int p2=getIntVal(string);
			if(p1==p2){
				return true;
			}
			else{
				return false;
			}
		}
	}

	private static int getIntVal(String port) {
		try{
			return Integer.parseInt(port);
		}
		catch(Exception e){
			return -1;
		}
	}

	private static boolean ifMatchIP(Rule r, String ip) {
		if(r.allIp){
			return true;
		}
		else if(r.hasPrefix){
			//need to add stuff for prefix matching
			Integer ip_add =convertStringToIntegerIp(ip);
			if(longMatchIP(r.IPadd,ip_add,r.ipPrefix)){
				return true;
			}else{
				return false;
			}
		}
		else{

			if(r.ipRaw.equals(ip))
				return true;
			else
				return false;
		}

	}


	private static boolean longMatchIP(Integer padd, Integer ip_add, int ipPrefix) {
		int p1= padd >> (32-ipPrefix);
		int p2=ip_add >>(32-ipPrefix);
		if(p1==p2){
			return true;
		}
		else{
			return false;
		}
	}

	public static Integer convertStringToIntegerIp(String ip) {

		String[] parts = ip.split("\\.");

		return (Integer.valueOf(parts[0]) << 24) +

		(Integer.valueOf(parts[1]) << 16) +

		(Integer.valueOf(parts[2]) << 8) +

		(Integer.valueOf(parts[3]));

	}




	public static int correctFormat(String message){
		String sections[]=new String[6];
		sections=message.split(" ");
		if(sections.length!=5){
			//System.out.println("format not 5");
			return 0;
		}
		else{
			if(isIPAddress(sections[0]) && isIPAddress(sections[1]) && isPort(sections[2]) && isPort(sections[3])&& validFlag(sections[4])){
				return 5;
			}
			else{
				//		System.out.println("else format");
				return 0;
			}
		}
	}

	private static boolean validFlag(String flag) {
		if(flag.equalsIgnoreCase("start")||flag.equalsIgnoreCase("end")||flag.equalsIgnoreCase("continue"))
		{
			return true;	
		}
		else{
			return false;
		}
	}

	private static boolean isPort(String port) {
		try {
			int temp = Integer.parseInt(port);
			if(temp<=0 || temp>=65536){
				return false;
			}
			else{
				return true;
			}
		} catch (Exception e) {
			return false;
		}
	}

	private static boolean isIPAddress(String ip) {
		StringTokenizer st = new StringTokenizer(ip, ".");
		//System.out.println(st.countTokens());
		if(st.countTokens()!=4){
			return false;
		}
		while (st.hasMoreTokens()) {
			try{
				int tempi =Integer.parseInt(st.nextToken());
				if(tempi<0 || tempi>255){
					return false;
				}
			}
			catch(Exception e){
				return false;
			}
		}
		return true;
	}

}
